/*
	This file is part of Erosion, a clone of Qix
	Copyright (c) 2009 Bill Whitacre

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <math.h>

#include "rat_physics.h"

#include "system.h"
#include "ortho.h"
#include "projection.h"
#include "font.h"
#include "primitives.h"
#include "draw.h"
#include "shader.h"
#include "fbo.h"

#include "qsort.h"
#include "gnomesort.h"

//#define DEBUG_TILES
//#define DEBUG_DRAW_CURRENT_NODE
//#define DRAW_EXTRA_STUFF

#define DEFINE_COLOR(name,r,g,b,a) \
	const float name##_COLOR[4]={(r),(g),(b),(a)}

#ifdef _MSC_VER
#	define DEFINE_DEBUG_DESC(name,str) \
		const char *_debug_desc_##name##=##str
#endif

#define LICENSE_TEXT "\
Permission is hereby granted, free of charge, to any person obtaining a copy\n\
of this software and associated documentation files (the \"Software\"), to deal\n\
in the Software without restriction, including without limitation the rights\n\
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\
copies of the Software, and to permit persons to whom the Software is\n\
furnished to do so, subject to the following conditions:\n\
\n\
The above copyright notice and this permission notice shall be included in\n\
all copies or substantial portions of the Software.\n\
\n\
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n\
THE SOFTWARE.\n"

#define ORIENTATION_HORZ			0xF0
#define ORIENTATION_VERT			0x0F

#define DIRECTION_POS				1
#define DIRECTION_NEG				-1

#define GAME_FLAG_NEAR_NODE			0x001

#define GAME_FLAG_USE_GAMEPAD		0x002
#define GAME_FLAG_DO_GAMEPAD		0x004

#define GAME_FLAG_INPUT_UP			0x008
#define GAME_FLAG_INPUT_DOWN		0x010
#define GAME_FLAG_INPUT_LEFT		0x020
#define GAME_FLAG_INPUT_RIGHT		0x040
#define GAME_FLAG_INPUT_START		0x080
#define GAME_FLAG_INPUT_BACK		0x100
#define GAME_FLAG_INPUT_SLOWDRAW	0x200

#define GAME_FLAG_INPUTS \
	(GAME_FLAG_INPUT_UP|GAME_FLAG_INPUT_DOWN|GAME_FLAG_INPUT_LEFT| \
	GAME_FLAG_INPUT_RIGHT|GAME_FLAG_INPUT_START|GAME_FLAG_INPUT_BACK| \
	GAME_FLAG_INPUT_SLOWDRAW)

typedef struct SKeyPair
{
	unsigned short map_flag;
	unsigned short map_key;
} SKeyPair;

const unsigned NUM_INPUTS=7;
SKeyPair key_map[7]=
{
	{GAME_FLAG_INPUT_UP,RAT_KEY_KEY_W},
	{GAME_FLAG_INPUT_DOWN,RAT_KEY_KEY_S},
	{GAME_FLAG_INPUT_LEFT,RAT_KEY_KEY_A},
	{GAME_FLAG_INPUT_RIGHT,RAT_KEY_KEY_D},
	{GAME_FLAG_INPUT_START,RAT_KEY_RETURN},
	{GAME_FLAG_INPUT_BACK,RAT_KEY_ESCAPE},
	{GAME_FLAG_INPUT_SLOWDRAW,RAT_KEY_SPACE}
};

SKeyPair pad_map[7]=
{
	{GAME_FLAG_INPUT_UP,RAT_GAMEPAD_DPAD_UP},
	{GAME_FLAG_INPUT_DOWN,RAT_GAMEPAD_DPAD_DOWN},
	{GAME_FLAG_INPUT_LEFT,RAT_GAMEPAD_DPAD_LEFT},
	{GAME_FLAG_INPUT_RIGHT,RAT_GAMEPAD_DPAD_RIGHT},
	{GAME_FLAG_INPUT_START,RAT_GAMEPAD_START},
	{GAME_FLAG_INPUT_BACK,RAT_GAMEPAD_BACK},
	{GAME_FLAG_INPUT_SLOWDRAW,
		RAT_GAMEPAD_A|RAT_GAMEPAD_B|
		RAT_GAMEPAD_X|RAT_GAMEPAD_Y}
};

typedef enum EInputNameIndices
{
	E_INPUT_UP=0,
	E_INPUT_DOWN,
	E_INPUT_LEFT,
	E_INPUT_RIGHT,
	E_INPUT_START,
	E_INPUT_BACK,
	E_INPUT_SLOWDRAW
} EInputNameIndices;

const char *controller_input_names[7]=
{
	"UP","DOWN","LEFT","RIGHT",
	"START","BACK","A/B/X/Y"
};
const char *keyboard_input_names[7]=
{
	"W","S","A","D",
	"ENTER","ESCAPE","SPACEBAR"
};

#define WIDTH_MAX					(int)USHRT_MAX
#define HEIGHT_MAX					(int)(USHRT_MAX/2+USHRT_MAX/4)

const vector2 null_vec={0,0};

const unsigned int NUM_LONGS_IN_256=256/sizeof(long);

DEFINE_COLOR(CLRS,0.0f,0.0f,0.0f,1.0f);
DEFINE_COLOR(OLDL,0.5f,0.5f,0.8f,0.8f);
DEFINE_COLOR(NEWL,0.0f,0.0f,1.8f,1.0f);
DEFINE_COLOR(DRAW,0.1f,2.8f,0.1f,1.0f);
DEFINE_COLOR(TEXT,0.8f,1.5f,7.0f,1.0f);
DEFINE_COLOR(RECT,0.0f,0.0f,0.0f,1.0f);
DEFINE_COLOR(FILL,0.1f,0.1f,0.2f,1.0f);

const unsigned int VERSION_MAJOR=0;
const unsigned int VERSION_MINOR=1;
const char *VERSION_COMMENT="alpha";

#define _PLAYER_SPEED 64
#define _MIN_SEG_LENGTH 256

const short PLAYER_SPEED=_PLAYER_SPEED;
const short PLAYER_NODE_JUMP_THRESHOLD=64;

const short MIN_SEG_LENGTH=_MIN_SEG_LENGTH;

const int num_levels_init_rays=6;
const unsigned char init_num_rays[6]={1,2,3,3,3,4};

const double level_area=((double)WIDTH_MAX/2.0)*((double)HEIGHT_MAX/2.0);

const double THRUST_UPDATE_DELAY=1.5;

int is_colliding=0;
clock_t rumble_start;

typedef enum EGameState
{
	GameState_Quit=-1,
	GameState_GameOver=0,
	GameState_GameMenu,
	GameState_OptionsMenu,
	GameState_EndGame,
	GameState_Playing,
	GameState_NextStage,

	force_word64_0=0xFFFFFFFF
} EGameState;

typedef enum EPlayState
{
	PlayState_LineWalk=0,
	PlayState_DrawArea,
	PlayState_EnterName,

	force_word64_1=0xFFFFFFFF
} EPlayState;

typedef struct SThruster
{
	vector2 offset;
	vector2 thrust;
} SThruster;

typedef struct SBodyBin
{
	rat_body *body;
	struct SBodyBin *next;
} SBodyBin;

typedef struct SConstraintBin
{
	int is_spring;
	void *constraint;
	struct SConstraintBin *next;
} SConstraintBin;

typedef struct SElectricRay
{
	SBodyBin *bodies;
	SConstraintBin *constraints;
	SThruster thruster;
} SElectricRay;

typedef struct SQuad
{
	unsigned short x0,y0,x1,y1;
} SQuad;

typedef struct SNode
{
	unsigned short x,y;

	struct SSegment *seg0,*seg1;
} SNode;

typedef struct SSegment
{
	unsigned char orientation;

	SNode *node0,*node1;
} SSegment;

typedef struct SGame
{
	rat_block_allocator *bal;
	rat_world *phys_world;

	EGameState game_state;
	EPlayState play_state;

	unsigned int flags;
	unsigned short level;
	unsigned long score;

	unsigned long num_segs,num_draw_segs;
	unsigned long num_nodes,num_draw_nodes;

	SSegment **segs,**draw_segs;
	SNode **nodes,**draw_nodes;

	SSegment *current_seg;
	SNode *current_node;

	short velocity;
	unsigned short position_x,position_y;
	char draw_direction;

	unsigned char num_rays;
	SElectricRay *rays;

	double enclosure_area;
	double capture_percent;

	unsigned int num_fill_quads;
	SQuad *fill_quads;

	unsigned int num_wall_quads;
	rat_body **wall_quads;
	int display_list;

	clock_t last_time_update;

	vector2 firing_direction;

	char **input_names;
} SGame;

void debug_dump_structs(SGame *game);

void ray_collision_occurs(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
void ray_collision_persists(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
void ray_collision_ending(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);

void update_keybuf(unsigned char *keys);
void update_inputs(SGame *game,unsigned char *keys);

int initialize_field(SGame *game,unsigned short level,unsigned long score);
void destroy_field(SGame *game);

void render_field(SGame *game);
void render_lines(SGame *game);
void render_player(SGame *game);

int update_game(SGame *game,unsigned char *keys);

int start_draw(SGame *game,int lateral_movement);
int finish_draw(SGame *game,char orientation,SSegment *collision_seg);

void tesselate_main_loop(SGame *game);

void game_over_effect(const float ctr);

#ifdef _MSC_VER
// debug messages for line walk
DEFINE_DEBUG_DESC(seg_vert,"vertical seg");
DEFINE_DEBUG_DESC(seg_horz,"horizontal seg");
DEFINE_DEBUG_DESC(node_prox,"; node is jump proxy!");

// debug messages for area draw
DEFINE_DEBUG_DESC(draw,"drawing ");
DEFINE_DEBUG_DESC(draw_vert,"vertically ");
DEFINE_DEBUG_DESC(draw_horz,"horizontally ");
DEFINE_DEBUG_DESC(draw_pos,"positive");
DEFINE_DEBUG_DESC(draw_neg,"negative");
#else
// debug messages for line walk
const char *_debug_desc_seg_vert="vertical seg";
const char *_debug_desc_seg_horz="horizontal seg";
const char *_debug_desc_node_prox="; node is jump proxy!";

// debug messages for area draw
const char *_debug_desc_draw="drawing ";
const char *_debug_desc_draw_vert="vertically ";
const char *_debug_desc_draw_horz="horizontally ";
const char *_debug_desc_draw_pos="positive";
const char *_debug_desc_draw_neg="negative";
#endif

const char *captured_str="Area Captured:";
const char *score_str="Score:";

int use_bloom=1;

int main(int argc,char *argv[])
{
	clock_t oldtime=clock();
	clock_t newtime=oldtime;

	int run=1,backdown=0,startdown=0,slowdown=0;
	SGame *game;
	unsigned char *keys;

	rat_fbo *game_surface_fbo=NULL;
	rat_fbo *hipass_surface_fbo=NULL;
	rat_fbo *bloom_surface_fbo=NULL;

	rat_shader *bloom_shader=NULL;
	rat_shader *hipass_shader=NULL;

	rat_texture_font *texfont=NULL;
	rat_glyph_font *glyfont=NULL;

	rat_stack_allocator *sal;

	float game_over_ctr=0.0f;
	vector2 game_over_pos=null_vec;

	srand((unsigned int)time(NULL));

	printf("%sv%u.%u %s\n\n","* * * E R O S I O N * * *      ",
		VERSION_MAJOR,VERSION_MINOR,VERSION_COMMENT);

	printf("%s\n\n%s\n\n%s\n%s\n\n",
		"By Bill Whitacre (C) 2009",
		LICENSE_TEXT,
		"Using RAT system utilities by Bill Whitacre",
		"A game inspired by Qix.");

	if (!rat_physics_is_init())
	{
		if (!rat_physics_init())
		{
			printf("%s","Failed to initialize Rat Physics engine!\n");
			fflush(stdout);
			return 1;
		}
	}

	if (!rat_start_system_core("Erosion",800,600,16,0/*1*/,0))
	{
		printf("%s","Couldn't create a video mode!  Does your machine support OpenGL?\n");
		return 1;
	}

	if (!rat_start_font_system())
	{
		printf("%s","Failed to start the font system.\n   Are \"freetype6.dll\" and \"zlib1.dll\" both there?\n");
		return 1;
	}

	printf("%c",'\n');

	glyfont=NULL;
	glyfont=rat_glyph_font_load("small.ttf",16);
	if (!glyfont)
	{
		printf("The font is missing, asshat!\n");
		return 1;
	}

	texfont=rat_texture_font_from_glyph_font(glyfont);
	rat_set_text_color(TEXT_COLOR);

	game_surface_fbo	= rat_fbo_create(1024,1024,0);
	hipass_surface_fbo	= rat_fbo_create(1024,1024,0);
	bloom_surface_fbo	= rat_fbo_create(1024,1024,0);

	bloom_shader		= rat_load_shader("shaders","bloom_blit",stdout);
	hipass_shader		= rat_load_shader("shaders","hipass_blit",stdout);

	if (!(game_surface_fbo||hipass_surface_fbo||bloom_surface_fbo||bloom_shader||hipass_shader))
	{
		if (bloom_shader)		rat_destroy_shader(bloom_shader);
		if (hipass_shader)		rat_destroy_shader(hipass_shader);

		if (game_surface_fbo)	rat_fbo_destroy(game_surface_fbo);
		if (hipass_surface_fbo)	rat_fbo_destroy(hipass_surface_fbo);
		if (bloom_surface_fbo)	rat_fbo_destroy(bloom_surface_fbo);
	}

	sal=rat_stack_allocator_create();

	game=(SGame *)rat_stack_alloc(sal,sizeof(SGame));
	keys=(unsigned char *)rat_stack_alloc(sal,256);
	memset(keys,0,256);

	rat_ortho_set_mode(rat_ortho_vp_vres);
	rat_ortho_set_virtual_res(800,600);

	glEnable(GL_BLEND);
	glEnable(GL_LINE_SMOOTH);

	if (rat_is_controller_connected(0))
	{
		printf("%s","Gamepad found!  Using gamepad.\n");
		game->flags|=GAME_FLAG_USE_GAMEPAD|GAME_FLAG_DO_GAMEPAD;
		game->input_names=(char **)controller_input_names;
	}
	else
		game->input_names=(char **)keyboard_input_names;

#ifdef DRAW_EXTRA_STUFF
	rat_pretty_draw=-1;
#endif

	do
	{
		// all purpose workspace buffer
		char *apbuf=(char *)rat_stack_alloc(sal,1024);

		rat_ortho_set_mode(rat_ortho_vp_vres);
		rat_ortho_set_virtual_res(800,600);

		// bind the surface we're going to draw to
		rat_fbo_bind(use_bloom?game_surface_fbo:NULL);

		glClearColor(CLRS_COLOR[0],CLRS_COLOR[1],CLRS_COLOR[2],CLRS_COLOR[3]);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		switch (game->game_state)
		{
		case GameState_Quit:
			run=0;
			break;
		case GameState_EndGame:
			printf("%s","Game over, man.  GAME OVER!\n");
			destroy_field(game);
			glDeleteLists(game->display_list,1);

			game_over_pos=null_vec;

			game->game_state=GameState_GameOver;

			break;
		case GameState_GameOver:
			rat_ortho_push();

			game_over_effect(game_over_ctr);

			strcpy(apbuf,"GAME OVER");

			newtime=clock();
			game_over_ctr+=(((double)newtime-(double)oldtime)/(double)CLOCKS_PER_SEC)*2.0;
			oldtime=newtime;

			game_over_pos.y=sin(game_over_ctr*2.0)*50.0;
			game_over_pos.x=cos(game_over_ctr)*100.0;

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
				glTranslated(game_over_pos.x+400.0,game_over_pos.y+300.0,0);
				glScalef(5,5,1);
				rat_texture_font_render_text(texfont,
					-rat_texture_font_text_length(texfont,apbuf)*0.5,
					-rat_texture_font_height(texfont)*0.5,apbuf);
			glPopMatrix();

			strcpy(apbuf,"Press <");
			strcat(apbuf,game->input_names[E_INPUT_START]);
			strcat(apbuf,">");

			glPushMatrix();
				glTranslated(400.0,595.0,0);
				glScalef(2,2,1);
				rat_texture_font_render_text(texfont,
					-rat_texture_font_text_length(texfont,apbuf)*0.5,
					-rat_texture_font_height(texfont),apbuf);
			glPopMatrix();

			rat_ortho_pop();

			if (game->flags&GAME_FLAG_INPUT_START)
				startdown=1;
			else if (startdown==1&&!(game->flags&GAME_FLAG_INPUT_START))
			{
				startdown=0;
				game->game_state=GameState_GameMenu;
			}

			if (rat_is_controller_connected(0))
			{
				game->flags|=GAME_FLAG_USE_GAMEPAD;
				game->input_names=(char **)controller_input_names;
			}
			else
				game->input_names=(char **)keyboard_input_names;

			break;
		case GameState_GameMenu:
			rat_ortho_push();

			game_over_effect(game_over_ctr);

			newtime=clock();
			game_over_ctr+=(((double)newtime-(double)oldtime)/(double)CLOCKS_PER_SEC)*2.0;
			oldtime=newtime;

			strcpy(apbuf,"Press <");
			strcat(apbuf,game->input_names[E_INPUT_START]);
			strcat(apbuf,"> to begin game.");

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
				glTranslated(400.0,595.0,0);
				glScalef(1.5,1.5,1);
				rat_texture_font_render_text(texfont,
					-rat_texture_font_text_length(texfont,apbuf)*0.5,
					-rat_texture_font_height(texfont),apbuf);
			glPopMatrix();

			strcpy(apbuf,"Press <");
			strcat(apbuf,game->input_names[E_INPUT_SLOWDRAW]);
			strcat(apbuf,"> for options.");

			glPushMatrix();
				glTranslated(400.0,595.0,0);
				glScalef(1.5,1.5,1);
				rat_texture_font_render_text(texfont,
					-rat_texture_font_text_length(texfont,apbuf)*0.5,
					-rat_texture_font_height(texfont)*2.0,apbuf);
			glPopMatrix();

			rat_ortho_pop();

			if (game->flags&GAME_FLAG_INPUT_SLOWDRAW)
				slowdown=1;
			else if (slowdown==1&&!(game->flags&GAME_FLAG_INPUT_SLOWDRAW))
			{
				slowdown=0;
				game->game_state=GameState_OptionsMenu;
			}

			if (game->flags&GAME_FLAG_INPUT_START)
				startdown=1;
			else if (startdown==1&&!(game->flags&GAME_FLAG_INPUT_START))
			{
				startdown=0;

				initialize_field(game,0,0);
				game->display_list=glGenLists(1);
				glNewList(game->display_list,GL_COMPILE);
					render_field(game);
				glEndList();

				game->game_state=GameState_Playing;
			}

			break;
		case GameState_OptionsMenu:
			rat_ortho_push();

			game_over_effect(game_over_ctr);

			newtime=clock();
			game_over_ctr+=(((double)newtime-(double)oldtime)/(double)CLOCKS_PER_SEC)*2.0;
			oldtime=newtime;

			strcpy(apbuf,"<");
			strcat(apbuf,game->input_names[E_INPUT_SLOWDRAW]);
			strcat(apbuf,"> to toggle bloom: ");
			strcat(apbuf,use_bloom?"ON":"OFF");

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
				glTranslated(400.0,595.0,0);
				glScalef(1.5,1.5,1);
				rat_texture_font_render_text(texfont,
					-rat_texture_font_text_length(texfont,apbuf)*0.5,
					-rat_texture_font_height(texfont),apbuf);
			glPopMatrix();

			rat_ortho_pop();

			if (game->flags&GAME_FLAG_INPUT_SLOWDRAW)
				slowdown=1;
			else if (slowdown==1&&!(game->flags&GAME_FLAG_INPUT_SLOWDRAW))
			{
				slowdown=0;
				use_bloom=!use_bloom;
				if (use_bloom)
				{
					game_surface_fbo	= rat_fbo_create(1024,1024,0);
					hipass_surface_fbo	= rat_fbo_create(1024,1024,0);
					bloom_surface_fbo	= rat_fbo_create(1024,1024,0);

					bloom_shader		= rat_load_shader("shaders","bloom_blit",stdout);
					hipass_shader		= rat_load_shader("shaders","hipass_blit",stdout);
				}
				else
				{
					if (bloom_shader)		rat_destroy_shader(bloom_shader);
					if (hipass_shader)		rat_destroy_shader(hipass_shader);

					if (game_surface_fbo)	rat_fbo_destroy(game_surface_fbo);
					if (hipass_surface_fbo)	rat_fbo_destroy(hipass_surface_fbo);
					if (bloom_surface_fbo)	rat_fbo_destroy(bloom_surface_fbo);

					bloom_shader			= NULL;
					hipass_shader			= NULL;

					game_surface_fbo		= NULL;
					hipass_surface_fbo		= NULL;
					bloom_surface_fbo		= NULL;
				}
			}

			break;
		case GameState_Playing:
			rat_ortho_push();

			render_lines(game);
			render_player(game);

			if (game->play_state==PlayState_LineWalk)
			{
				if (game->current_seg->orientation&ORIENTATION_VERT)
					strcpy(apbuf,_debug_desc_seg_vert);
				else
					strcpy(apbuf,_debug_desc_seg_horz);

				if (game->flags&GAME_FLAG_NEAR_NODE)
					strcat(apbuf,_debug_desc_node_prox);

				rat_texture_font_render_text(texfont,5,5,apbuf);
			}
			else if (game->play_state==PlayState_DrawArea)
			{
				strcpy(apbuf,_debug_desc_draw);

				if (game->draw_segs[game->num_draw_segs-1]->orientation&ORIENTATION_VERT)
					strcat(apbuf,_debug_desc_draw_vert);
				else
					strcat(apbuf,_debug_desc_draw_horz);

				if (game->draw_direction>0)
					strcat(apbuf,_debug_desc_draw_pos);
				else
					strcat(apbuf,_debug_desc_draw_neg);

				rat_texture_font_render_text(texfont,5,5,apbuf);
			}

			sprintf(apbuf,"%s %03.1f%%   %s %8u",captured_str,game->capture_percent,score_str,game->score);
			rat_texture_font_render_text(texfont,5,570,apbuf);

			rat_ortho_pop();

			if (update_game(game,keys)) game->game_state=GameState_Quit;

			break;
		default:
			printf("%s","Invalid game state enum!\n");
			game->game_state=GameState_Quit;

			break;
		};

		if (game->game_state!=GameState_Quit)
		{
			rat_update_video();
			rat_handle_events();

			update_keybuf(keys);
			update_inputs(game,keys);

			if (rat_appterm_event()) game->game_state=GameState_Quit;
			if (game->flags&GAME_FLAG_INPUT_BACK)
				backdown=1;
			else if (backdown)
			{
				--game->game_state;
				backdown=0;
			}

			if ((keys[RAT_KEY_KEY_L]&0x0F)&&(keys[RAT_KEY_SHIFT]&0x0F)) debug_dump_structs(game);
		}

		if (is_colliding==0 && clock()-rumble_start>CLOCKS_PER_SEC/6)
		{
			rat_set_rumble(0,0,0);
		}

		if (use_bloom)
		{
			// bind the hipass surface
			rat_fbo_bind(hipass_surface_fbo);

			glClearColor(CLRS_COLOR[0],CLRS_COLOR[1],CLRS_COLOR[2],CLRS_COLOR[3]);
			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

			rat_ortho_push();

			rat_apply_shader(hipass_shader);

			glColor4f(1.0f,1.0f,1.0f,1.0f);

			glPushAttrib(GL_TEXTURE_BIT);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D,game_surface_fbo->texture);

				glBegin(GL_TRIANGLE_FAN);
					glTexCoord2d(0.0,600.0/1024.0);				glVertex2d(0.0,0.0);
					glTexCoord2d(800.0/1024.0,600.0/1024.0);	glVertex2d(800.0,0.0);
					glTexCoord2d(800.0/1024.0,0.0);				glVertex2d(800.0,600.0);
					glTexCoord2d(0.0,0.0);						glVertex2d(0.0,600.0);
				glEnd();

				glBindTexture(GL_TEXTURE_2D,0);
			glPopAttrib();

			// bind the screen surface
			rat_fbo_bind(NULL);

			glClearColor(CLRS_COLOR[0],CLRS_COLOR[1],CLRS_COLOR[2],CLRS_COLOR[3]);
			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

			rat_apply_shader(NULL);

			glColor4f(0.75f,0.75f,0.75f,1.0f);
			glPushAttrib(GL_TEXTURE_BIT);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D,game_surface_fbo->texture);

				glBegin(GL_TRIANGLE_FAN);
					glTexCoord2d(0.0,600.0/1024.0);				glVertex2d(0.0,0.0);
					glTexCoord2d(800.0/1024.0,600.0/1024.0);	glVertex2d(800.0,0.0);
					glTexCoord2d(800.0/1024.0,0.0);				glVertex2d(800.0,600.0);
					glTexCoord2d(0.0,0.0);						glVertex2d(0.0,600.0);
				glEnd();

				glBindTexture(GL_TEXTURE_2D,0);
			glPopAttrib();
			glColor4f(1.0f,1.0f,1.0f,1.0f);

			// bind the bloom surface
			rat_fbo_bind(bloom_surface_fbo);

			rat_ortho_set_mode(rat_ortho_vp_vres);
			rat_ortho_set_virtual_res(400,300);

			rat_ortho_push();

			glClearColor(CLRS_COLOR[0],CLRS_COLOR[1],CLRS_COLOR[2],CLRS_COLOR[3]);
			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

			rat_apply_shader(bloom_shader);

			glPushAttrib(GL_TEXTURE_BIT);
				glEnable(GL_TEXTURE_2D);
				glBindTexture(GL_TEXTURE_2D,hipass_surface_fbo->texture);

				glBegin(GL_TRIANGLE_FAN);
					glTexCoord2d(0.0,600.0/1024.0);				glVertex2d(0.0,0.0);
					glTexCoord2d(800.0/1024.0,600.0/1024.0);	glVertex2d(400.0,0.0);
					glTexCoord2d(800.0/1024.0,0.0);				glVertex2d(400.0,300.0);
					glTexCoord2d(0.0,0.0);						glVertex2d(0.0,300.0);
				glEnd();

				glBindTexture(GL_TEXTURE_2D,0);
			glPopAttrib();

			rat_ortho_pop();

			// bind the screen surface
			rat_fbo_bind(NULL);

			rat_apply_shader(NULL);

			glPushAttrib(GL_ENABLE_BIT);
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA,GL_ONE);

				glPushAttrib(GL_TEXTURE_BIT);
					glEnable(GL_TEXTURE_2D);
					glBindTexture(GL_TEXTURE_2D,bloom_surface_fbo->texture);

					glBegin(GL_TRIANGLE_FAN);
						glTexCoord2d(0.0,300.0/512.0);			glVertex2d(0.0,0.0);
						glTexCoord2d(400.0/512.0,300.0/512.0);	glVertex2d(800.0,0.0);
						glTexCoord2d(400.0/512.0,0.0);			glVertex2d(800.0,600.0);
						glTexCoord2d(0.0,0.0);					glVertex2d(0.0,600.0);
					glEnd();

					glBindTexture(GL_TEXTURE_2D,0);
				glPopAttrib();
			glPopAttrib();

			rat_ortho_pop();
		}

		rat_stack_dealloc(sal,(void *)apbuf);
	} while (run);

	printf("%c",'\n');

	if (glyfont)			rat_glyph_font_destroy(glyfont);
	if (texfont)			rat_texture_font_destroy(texfont);

	if (bloom_shader)		rat_destroy_shader(bloom_shader);
	if (hipass_shader)		rat_destroy_shader(hipass_shader);

	if (game_surface_fbo)	rat_fbo_destroy(game_surface_fbo);
	if (hipass_surface_fbo)	rat_fbo_destroy(hipass_surface_fbo);
	if (bloom_surface_fbo)	rat_fbo_destroy(bloom_surface_fbo);

	printf("%c",'\n');

	rat_stop_font_system();
	rat_stop_system_core();

	printf("\nBYE!\n");

	rat_stack_dealloc(sal,keys);
	rat_stack_dealloc(sal,game);
	rat_stack_allocator_destroy(sal);

	return 0;
}

void game_over_effect(const float ctr)
{
	register int i;

	for (i=0; i<20; ++i)
	{
		rat_set_color(2.0f,0.0f,0.0f,fabs(sin(((double)i/20.0*MATH_PI*2+ctr))));
		rat_draw_circle(
			400+cos(-((double)i/20.0*MATH_PI*2+ctr))*100.0*tan(sin(-ctr*0.001)*200),
			300+sin(-((double)i/20.0*MATH_PI*2+ctr))*100.0*tan(sin(-ctr*0.001)*200),
			fabs(sin((double)i/20.0*MATH_PI+ctr*0.25)*sin(cos((double)i/20.0*MATH_PI+ctr*0.1)*((double)i/20.0*MATH_PI*2+ctr*0.1)))*50.0,
			0.5,1);
	}

	for (i=0; i<20; ++i)
	{
		rat_set_color(2.0f,2.0f,0.0f,fabs(sin(((double)i/20.0*MATH_PI*2+ctr))));
		rat_draw_circle(
			400+tan((double)i/20.0*MATH_PI*2+ctr*0.01)+cos((double)i/20.0*MATH_PI*2+ctr)*200.0
				*tan(sin(-ctr*0.0005+i*0.001)*150),
			300+tan((double)i/20.0*MATH_PI*2+ctr*0.01)+sin((double)i/20.0*MATH_PI*2+ctr)*200.0
				*tan(sin(-ctr*0.0005+i*0.001)*150),
			20,0.5,1);
	}

	for (i=0; i<20; ++i)
	{
		rat_set_color(0.0f,0.0f,2.0f,fabs(sin(((double)i/20.0*MATH_PI*2+ctr))));
		rat_draw_circle(
			400-tan(-(double)i/20.0*MATH_PI*2+ctr*0.01)+cos(-(double)i/20.0*MATH_PI*2+ctr)*200.0
				*tan(sin(ctr*0.0005+i*0.001)*150),
			300-tan(-(double)i/20.0*MATH_PI*2+ctr*0.01)+sin(-(double)i/20.0*MATH_PI*2+ctr)*200.0
				*tan(sin(ctr*0.0005+i*0.001)*150),
			20,0.5,1);
	}

	for (i=0; i<10; ++i)
	{
		rat_set_color(0.0f,2.0f,0.0f,fabs(sin(((double)i/10.0*MATH_PI*2+ctr)*2.5)));
		rat_draw_circle(
			400+cos((double)i/20.0*MATH_PI*2+ctr)*150.0,
			300+sin((double)i/20.0*MATH_PI*2+ctr*2)*150.0,
			20,0.5,1);
	}

	for (i=0; i<10; ++i)
	{
		rat_set_color(2.0f,0.0f,0.0f,fabs(sin(((double)i/10.0*MATH_PI*2+ctr)*1.5)));
		rat_draw_circle(
			400+cos((double)i/20.0*MATH_PI*2+ctr*0.9)*150.0,
			300-sin((double)i/20.0*MATH_PI*2+ctr*2*0.9)*150.0,
			20,0.5,1);
	}

	for (i=0; i<10; ++i)
	{
		rat_set_color(2.0f,0.0f,0.0f,fabs(sin(((double)i/10.0*MATH_PI*2+ctr)*2)));
		rat_draw_circle(
			400-cos((double)i/20.0*MATH_PI*2+ctr*0.8)*150.0,
			300+sin((double)i/20.0*MATH_PI*2+ctr*2*0.8)*150.0,
			20,0.5,1);
	}

	for (i=0; i<10; ++i)
	{
		rat_set_color(2.0f,0.0f,0.0f,fabs(sin(((double)i/10.0*MATH_PI*2+ctr))));
		rat_draw_circle(
			400-cos((double)i/20.0*MATH_PI*2+ctr*0.7)*150.0,
			300-sin((double)i/20.0*MATH_PI*2+ctr*2*0.7)*150.0,
			20,0.5,1);
	}
}

static void __debug_dump(SGame *game,FILE *desc)
{
	register unsigned int i;

	printf("%c",'\n');
	for (i=0; i<game->num_segs; i++)
	{
		printf("seg  at 0x%08x -> { 0x%08x, 0x%08x, ori==0x%02x }\n",
			game->segs[i],game->segs[i]->node0,game->segs[i]->node1,
			game->segs[i]->orientation);

		printf("node at 0x%08x -> { 0x%08x, 0x%08x, pos==[%u,%u] }\n",
			game->nodes[i],game->nodes[i]->seg0,game->nodes[i]->seg1,
			game->nodes[i]->x,game->nodes[i]->y);
	}

	printf("\n%s==0x%08x\n%s==0x%08x\n\n",
		"current_seg",game->current_seg,
		"current_node",game->current_node);
}

void debug_dump_structs(SGame *game)
{
	__debug_dump(game,stdout);
}

void update_keybuf(unsigned char *keys)
{
	unsigned long *keyblocks=(unsigned long *)keys;
	unsigned char rawkeys[256];

	register unsigned int i;

	rat_get_key_buffer(rawkeys);

	for (i=0; i<NUM_LONGS_IN_256; i++)
		keyblocks[i]<<=4;

	for (i=0; i<RAT_KEY_CODE_COUNT; i++)
	{
		if (rawkeys[i])
			keys[i]|=0x0F;
		else
			keys[i]&=0xF0;
	}
}

void update_inputs(SGame *game,unsigned char *keys)
{
	register unsigned i;

	game->flags&=~GAME_FLAG_INPUTS;

	if (rat_is_controller_connected(0)&&(game->flags&GAME_FLAG_USE_GAMEPAD))
	{
		if (game->flags&GAME_FLAG_DO_GAMEPAD)
		{
			short x,y;

			for (i=0; i<NUM_INPUTS; ++i)
			{
				if (rat_is_button_pressed(0,pad_map[i].map_key)) game->flags|=pad_map[i].map_flag;
			}

			rat_left_thumbstick(0,&x,&y);

			if (y>SHRT_MAX/2)  game->flags|=GAME_FLAG_INPUT_UP;
			if (y<-SHRT_MAX/2) game->flags|=GAME_FLAG_INPUT_DOWN;
			if (x<-SHRT_MAX/2) game->flags|=GAME_FLAG_INPUT_LEFT;
			if (x>SHRT_MAX/2)  game->flags|=GAME_FLAG_INPUT_RIGHT;

			rat_right_thumbstick(0,&x,&y);

			game->firing_direction.x=(rat_real)x;
			game->firing_direction.y=(rat_real)-y;

			vector2_normalize(&(game->firing_direction));

			if (rat_is_button_pressed(0,RAT_GAMEPAD_A|RAT_GAMEPAD_B|RAT_GAMEPAD_X|RAT_GAMEPAD_Y))
				game->flags|=GAME_FLAG_INPUT_SLOWDRAW;
		}
	}
	else
		game->flags&=~GAME_FLAG_USE_GAMEPAD;

	// the reason this isn't an else is because there is a possibility
	// of entering both in some cases when the controller has just been
	// unplugged.
	if (!(game->flags&GAME_FLAG_USE_GAMEPAD))
	{
		float x,y;

		rat_get_mouse_position(&x,&y);

		game->firing_direction.x=(rat_real)x*800.0f-(50.0f+((float)game->position_x/(float)WIDTH_MAX)*700.0f);
		game->firing_direction.y=(rat_real)y*600.0f-(50.0f+((float)game->position_y/(float)HEIGHT_MAX)*500.0f);

		vector2_normalize(&(game->firing_direction));
	}

	for (i=0; i<NUM_INPUTS; ++i)
	{
		if (keys[key_map[i].map_key]&0x0F) game->flags|=key_map[i].map_flag;
	}
}

#define _NUM_INIT_LINES 4

const unsigned char init_orientation[_NUM_INIT_LINES]=
{
	ORIENTATION_VERT,ORIENTATION_HORZ,
	ORIENTATION_VERT,ORIENTATION_HORZ
};

const unsigned short init_xdims[_NUM_INIT_LINES]={0,0,         WIDTH_MAX, WIDTH_MAX};
const unsigned short init_ydims[_NUM_INIT_LINES]={0,HEIGHT_MAX,HEIGHT_MAX,0};

const int NUM_INIT_LINES=_NUM_INIT_LINES;

__inline static void initialize_walls(SGame *game)
{
	vector2 wallverts[4];
	vector2 tmpvec;

	rat_hull **wallhulls=(rat_hull **)rat_alloc(4*sizeof(rat_hull *));
	rat_body **walls=(rat_body **)rat_alloc(4*sizeof(rat_body *));

	vector2_set(wallverts,-10,-500.0/2.0);
	vector2_set(wallverts+1,-10,500.0/2.0);
	vector2_set(wallverts+2,10,500.0/2.0);
	vector2_set(wallverts+3,10,-500.0/2.0);

	vector2_set(&tmpvec,-10,500.0/2.0);
	wallhulls[0]=rat_hull_polygon_create(tmpvec,4,wallverts);
	vector2_set(&tmpvec,710.0,500.0/2.0);
	wallhulls[1]=rat_hull_polygon_create(tmpvec,4,wallverts);

	vector2_set(wallverts,-700.0/2.0,-10);
	vector2_set(wallverts+1,-700.0/2.0,10);
	vector2_set(wallverts+2,700.0/2.0,10);
	vector2_set(wallverts+3,700.0/2.0,-10);

	vector2_set(&tmpvec,700.0/2.0,-10);
	wallhulls[2]=rat_hull_polygon_create(tmpvec,4,wallverts);
	vector2_set(&tmpvec,700.0/2.0,510.0);
	wallhulls[3]=rat_hull_polygon_create(tmpvec,4,wallverts);

	walls[0]=rat_body_create(wallhulls[0],1.0,0.0,0.0,0.0);
	walls[1]=rat_body_create(wallhulls[1],1.0,0.0,0.0,0.0);
	walls[2]=rat_body_create(wallhulls[2],1.0,0.0,0.0,0.0);
	walls[3]=rat_body_create(wallhulls[3],1.0,0.0,0.0,0.0);

	rat_world_add_body(game->phys_world,walls[0]);
	rat_world_add_body(game->phys_world,walls[1]);
	rat_world_add_body(game->phys_world,walls[2]);
	rat_world_add_body(game->phys_world,walls[3]);

	rat_dealloc((void *)wallhulls);
	rat_dealloc((void *)walls);
}

__inline static void initialize_rays(SGame *game)
{
	register int i;

	game->num_rays=game->level<num_levels_init_rays?
		init_num_rays[game->level]:init_num_rays[num_levels_init_rays-1];

	game->rays=(SElectricRay *)rat_block_alloc(game->bal,sizeof(SElectricRay)*game->num_rays);
	for (i=0; i<game->num_rays; i++)
	{
		rat_hull *hull;
		float direction;
		vector2 pos;
		vector2 verts[4]=
		{
			-5.0f, 125.0f,
			 5.0f, 125.0f,
			 5.0f,-125.0f,
			-5.0f,-125.0f
		};

		game->rays[i].bodies=(SBodyBin *)rat_block_alloc(game->bal,sizeof(SBodyBin));
		game->rays[i].constraints=NULL;

		pos.x=((float)rand()/(float)RAND_MAX)*690.0f+5.0f;
		pos.y=((float)rand()/(float)RAND_MAX)*250.0f+125.0f;

		hull=rat_hull_polygon_create(pos,4,verts);
		game->rays[i].bodies->body=rat_body_create(hull,1.0f,0.0f,1.0f,rat_moi_polygon(1.0f,verts,4));
		game->rays[i].bodies->next=NULL;

		rat_hull_set_angle(game->rays[i].bodies->body->hull,((float)rand()/(float)RAND_MAX)*2.0f*MATH_PI);
		rat_world_add_body(game->phys_world,game->rays[i].bodies->body);

		game->rays[i].bodies->body->omega=1.0f;

		direction=((float)rand()/(float)RAND_MAX)*2.0f*MATH_PI;
		game->rays[i].bodies->body->velocity.x=cos(direction)*120.0f;
		game->rays[i].bodies->body->velocity.y=sin(direction)*120.0f;

		game->rays[i].thruster.offset.x=0.0f;
		game->rays[i].thruster.offset.y=120.0f;
	}
}

int initialize_field(SGame *game,unsigned short level,unsigned long score)
{
	register int i,j;
	unsigned int old_flags=game->flags;

	aabb bounds={-50.0f,-50.0f,750.0f,550.0f};
	vector2 cellsize={80.0f,80.0f};

	memset(game,0,sizeof(SGame));

	game->flags=old_flags;

	if (rat_is_controller_connected(0))
	{
		game->flags|=GAME_FLAG_USE_GAMEPAD;
		game->input_names=(char **)controller_input_names;
	}
	else
	{
		game->flags&=~GAME_FLAG_USE_GAMEPAD;
		game->input_names=(char **)keyboard_input_names;
	}

	game->level=level;
	game->score=score;

	game->bal=rat_block_allocator_create();
	game->phys_world=rat_world_create(bounds,rat_integrator_euler,
		32,rat_body_manager_type_array_table,(void *)&cellsize);

	rat_world_set_collision_occurs_callback(game->phys_world,ray_collision_occurs,(void *)game);
	rat_world_set_collision_persists_callback(game->phys_world,ray_collision_persists,(void *)game);
	rat_world_set_collision_ending_callback(game->phys_world,ray_collision_ending,(void *)game);

	game->last_time_update=clock()-(clock_t)(THRUST_UPDATE_DELAY*CLOCKS_PER_SEC);

	initialize_walls(game);

	printf("%s","\nInitializing game field...\n");

	initialize_rays(game);

	printf("%s","   Created electric rays.\n");

	game->num_fill_quads=1;
	game->fill_quads=(SQuad *)rat_alloc(sizeof(SQuad));
	game->fill_quads[0].x0=0;
	game->fill_quads[0].y0=0;
	game->fill_quads[0].x1=WIDTH_MAX;
	game->fill_quads[0].y1=HEIGHT_MAX;

	game->enclosure_area=level_area;

	game->num_segs=NUM_INIT_LINES;
	game->num_nodes=NUM_INIT_LINES;

	game->segs=NULL;
	game->segs=(SSegment **)rat_alloc(sizeof(SSegment *)*NUM_INIT_LINES);
	if (!game->segs)
	{
		printf("%s","Failed to allocate memory for segments list!\n");
		return 0;
	}

	game->nodes=NULL;
	game->nodes=(SNode **)rat_alloc(sizeof(SNode *)*NUM_INIT_LINES);
	if (!game->nodes)
	{
		printf("%s","Failed to allocate memory for node list!\n");
		return 0;
	}

	for (i=0; i<NUM_INIT_LINES; i++)
	{
		game->segs[i]=NULL;
		game->segs[i]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
		if (!game->segs[i])
		{
			for (j=0; j<i; j++)
				rat_block_dealloc(game->bal,(void *)game->segs[j],sizeof(SSegment));
			rat_dealloc((void *)game->segs);
			rat_dealloc((void *)game->nodes);
			printf("%s","Failed to allocate memory for segment!\n");
			return 0;
		}
	}

	for (i=0; i<NUM_INIT_LINES; i++)
	{
		game->nodes[i]=NULL;
		game->nodes[i]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));
		if (!game->nodes[i])
		{
			for (j=0; j<i; j++)
				rat_block_dealloc(game->bal,(void *)game->nodes[j],sizeof(SNode));
			for (j=0; j<NUM_INIT_LINES; j++)
				rat_block_dealloc(game->bal,(void *)game->segs[j],sizeof(SSegment));

			rat_dealloc((void *)game->segs);
			rat_dealloc((void *)game->nodes);
			printf("%s","Failed to allocate memory for node!\n");
			return 0;
		}
	}

	for (i=0; i<NUM_INIT_LINES; i++)
	{
		game->segs[i]->orientation=init_orientation[i];
		game->segs[i]->node0=game->nodes[i];
		game->segs[i]->node1=game->nodes[(i+1)%NUM_INIT_LINES];

		game->nodes[i]->x=init_xdims[i];
		game->nodes[i]->y=init_ydims[i];

		game->nodes[i]->seg0=game->segs[(i-1)>-1?(i-1):(NUM_INIT_LINES-1)];
		game->nodes[i]->seg1=game->segs[i];
	}

	printf("%s","   Created segment loop.\n");

	game->current_node=game->nodes[0];
	game->current_seg=game->nodes[0]->seg0;
	game->position_x=game->position_y=0;

	printf("%s","...DONE!\n");

	// set world damping
	rat_world_set_damping(game->phys_world,0.9f);

	return 1;
}

void destroy_field(SGame *game)
{
	register unsigned int i;

	printf("\nDestroying game field...\n");

	for (i=0; i<game->num_rays; i++)
	{
		SBodyBin *bodybin=game->rays[i].bodies;
		SConstraintBin *consbin=game->rays[i].constraints;

		for (; consbin; consbin=consbin->next)
		{
			if (consbin->constraint)
			{
				if (consbin->is_spring)
				{
					rat_world_remove_and_destroy_spring(game->phys_world,(rat_spring *)consbin->constraint);
					rat_world_update(game->phys_world,0.0f,1);
				}
				else
				{
					rat_world_remove_and_destroy_constraint(game->phys_world,(rat_constraint *)consbin->constraint);
					rat_world_update(game->phys_world,0.0f,1);
				}
			}
		}

		for (; bodybin; bodybin=bodybin->next)
		{
			if (bodybin->body)
			{
				rat_world_remove_and_destroy_body(game->phys_world,bodybin->body);
				rat_world_update(game->phys_world,0.0f,1);
			}
		}


		rat_world_update(game->phys_world,0.0f,1);
	}
	rat_block_dealloc(game->bal,(void *)game->rays,sizeof(SElectricRay)*game->num_rays);

	rat_world_free_entities(game->phys_world);
	rat_world_destroy(game->phys_world);

	rat_dealloc((void *)game->fill_quads);

	printf("   Deleted electric rays.\n");

	if (game->play_state==PlayState_DrawArea)
	{
		for (i=0; i<game->num_draw_segs; i++)
			rat_block_dealloc(game->bal,(void *)game->draw_segs[i],sizeof(SSegment));
		printf("   Deleted WIP segments.\n");

		for (i=0; i<game->num_draw_nodes; i++)
			rat_block_dealloc(game->bal,(void *)game->draw_nodes[i],sizeof(SNode));
		printf("   Deleted WIP nodes.\n");

		rat_dealloc((void *)game->draw_segs);
		rat_dealloc((void *)game->draw_nodes);

		printf("   Deleted WIP lists.\n...DONE!\n");
	}

	for (i=0; i<game->num_segs; i++)
		rat_block_dealloc(game->bal,(void *)game->segs[i],sizeof(SSegment));
	printf("   Deleted segments.\n");

	for (i=0; i<game->num_nodes; i++)
		rat_block_dealloc(game->bal,(void *)game->nodes[i],sizeof(SNode));
	printf("   Deleted nodes.\n");

	rat_dealloc((void *)game->segs);
	rat_dealloc((void *)game->nodes);

	printf("   Deleted lists.\n...DONE!\n");

	rat_block_allocator_destroy(game->bal);
}

void render_field(SGame *game)
{
	register unsigned int i;
	char kickout=0;

	SSegment *this_seg=game->current_seg;
	SNode *last_node=game->current_seg->node0;

	// draw the filled area quad.  (the unfilled area will be drawn with tesselated rectangles)
	rat_set_color(RECT_COLOR[0],RECT_COLOR[1],RECT_COLOR[2],RECT_COLOR[3]);
	rat_draw_rect(50,50,750,550,1);

	// draw the empty area quads.
	rat_set_color(FILL_COLOR[0],FILL_COLOR[1],FILL_COLOR[2],FILL_COLOR[3]);
	for (i=0; i<game->num_fill_quads; i++)
	{
		rat_draw_rect(
			50+((float)game->fill_quads[i].x0/(float)WIDTH_MAX)*700,
			50+((float)game->fill_quads[i].y0/(float)HEIGHT_MAX)*500,
			50+((float)game->fill_quads[i].x1/(float)WIDTH_MAX)*700,
			50+((float)game->fill_quads[i].y1/(float)HEIGHT_MAX)*500,
			1);
	}

#ifndef DEBUG_TILES
	// draw all segments in the active structure
	rat_line_width(3.0f);
	rat_set_color(OLDL_COLOR[0],OLDL_COLOR[1],OLDL_COLOR[2],OLDL_COLOR[3]);
	for (i=0; i<game->num_segs; i++)
	{
		rat_draw_line(
			50+((float)game->segs[i]->node0->x/(float)WIDTH_MAX)*700,
			50+((float)game->segs[i]->node0->y/(float)HEIGHT_MAX)*500,
			50+((float)game->segs[i]->node1->x/(float)WIDTH_MAX)*700,
			50+((float)game->segs[i]->node1->y/(float)HEIGHT_MAX)*500);
	}
#endif

	// draw the segments on the active loop
	rat_line_width(4.0f);
	rat_set_color(NEWL_COLOR[0],NEWL_COLOR[1],NEWL_COLOR[2],NEWL_COLOR[3]);
	for (;;)
	{
		SNode *next_node;

		rat_draw_line(
			50+((float)this_seg->node0->x/(float)WIDTH_MAX)*700,
			50+((float)this_seg->node0->y/(float)HEIGHT_MAX)*500,
			50+((float)this_seg->node1->x/(float)WIDTH_MAX)*700,
			50+((float)this_seg->node1->y/(float)HEIGHT_MAX)*500);

		next_node=this_seg->node0==last_node?this_seg->node1:this_seg->node0;
		this_seg=next_node->seg0==this_seg?next_node->seg1:next_node->seg0;
		last_node=next_node;

		if ((this_seg==game->current_seg)&&kickout) break;
		kickout=-1;
	}
}

// draw the segments as lines.  all established segments are in
// black, and the main loop you can travel has a finer blue
// line loop over it, and any new line you're drawing is in red
void render_lines(SGame *game)
{
	register unsigned int i;

	// call the baked display list
	glCallList(game->display_list);

	rat_line_width(1.0f);

#ifdef DEBUG_DRAW_CURRENT_NODE
	// draw the current node
	rat_set_color(0.0f,0.0f,1.0f,1.0f);
	rat_draw_circle(
		50+((float)game->current_node->x/(float)WIDTH_MAX)*700,
		50+((float)game->current_node->y/(float)HEIGHT_MAX)*500,20,1,0);
#endif

	// draw all the segments in the line in progress
	if (game->play_state==PlayState_DrawArea)
	{
		rat_line_width(3.0f);
		rat_set_color(DRAW_COLOR[0],DRAW_COLOR[1],DRAW_COLOR[2],DRAW_COLOR[3]);

		for (i=0; i<game->num_draw_segs; i++)
		{
			rat_draw_line(
				50+((float)game->draw_segs[i]->node0->x/(float)WIDTH_MAX)*700,
				50+((float)game->draw_segs[i]->node0->y/(float)HEIGHT_MAX)*500,
				50+((float)game->draw_segs[i]->node1->x/(float)WIDTH_MAX)*700,
				50+((float)game->draw_segs[i]->node1->y/(float)HEIGHT_MAX)*500);
		}
	}

	// draw the electric rays
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(50.0f,50.0f,0.0f);

	rat_line_width(3.0);

	for (i=0; i<game->num_rays; i++)
	{
		SBodyBin *bodybin=game->rays[i].bodies;
		SConstraintBin *consbin=game->rays[i].constraints;

		rat_real thruster_angle=rat_hull_get_angle(bodybin->body->hull);
		vector2 thruster_global_pos=
			vector2_local_to_world(game->rays[i].thruster.offset,
				rat_hull_get_pos(bodybin->body->hull),thruster_angle);
		vector2 thruster_global_thrust=vector2_multf(vector2_local_to_world
			(game->rays[i].thruster.thrust,null_vec,thruster_angle),30.0f);

		vector2 thruster_line_2=vector2_add(thruster_global_thrust,thruster_global_pos);

		rat_set_color(2.2,0.2,0.2,1.0);

		for (; bodybin; bodybin=bodybin->next)
		{
			if (bodybin->body)
				rat_draw_body(bodybin->body,2);
		}

		for (; consbin; consbin=consbin->next)
		{
			if (consbin->constraint)
			{
				if (consbin->is_spring)
					rat_draw_spring((rat_spring *)consbin->constraint);
				else
					rat_draw_constraint((rat_constraint *)consbin->constraint);
			}
		}

		rat_set_color(0.0f,1.0f,0.0f,1.0f);
		rat_draw_line(thruster_global_pos.x,thruster_global_pos.y,thruster_line_2.x,thruster_line_2.y);
	}

#ifdef DEBUG_TILES
	// draw the boxes for debugging purposes
	for (i=0; i<game->num_wall_quads; i++)
	{
		rat_draw_body(game->wall_quads[i],2);
	}
#endif

	glPopMatrix();
}

// draw the player
void render_player(SGame *game)
{
	vector2 position=
	{
		50+((float)game->position_x/(float)WIDTH_MAX)*700,
		50+((float)game->position_y/(float)HEIGHT_MAX)*500
	};
	vector2 posvec=vector2_add(position,vector2_multf(game->firing_direction,50));

	rat_set_color(2.0f,0.0f,0.0f,1.0f);
	rat_draw_circle(position.x,position.y,10,1,1);

	rat_set_color(0.0f,0.0f,2.0f,1.0f);
	rat_draw_line(position.x,position.y,posvec.x,posvec.y);
}

// divide one sement in to two by adding a node somewhere
// in between.  we assume that the values passed are valid.
SNode *divide_segment(SGame *game,SSegment *seg,unsigned short newx,unsigned short newy)
{
	const unsigned char NODE0_IS_0=0x1;
	const unsigned char NODE1_IS_0=0x2;
	unsigned char node_links=0;

	SSegment *newseg0=NULL;
	SSegment *newseg1=NULL;

	SNode *node0=seg->node0;
	SNode *node1=seg->node1;
	SNode *newnode=NULL;

	if (!node0)
	{
		printf("%s%08x%s",
			"structure corruption detected: seg<0x",seg,
			">->node0 was NULL!\n");
		return NULL;
	}

	if (!node1)
	{
		printf("%s%08x%s",
			"structure corruption detected: seg<0x",seg,
			">->node1 was NULL!\n");
		return NULL;
	}

	if (node0==node1)
	{
		printf("%s%08x%s%08x%s",
			"structure corruption detected: seg<0x",seg,
			">->node1 links doubly to node<0x",node0,">!\n");
		return NULL;
	}

	node_links|=node0->seg0==seg?NODE0_IS_0:0;
	node_links|=node1->seg0==seg?NODE1_IS_0:0;

	newseg1=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
	if (!newseg1)
	{
		printf("%s","Failed to allocate memory for new segment!\n");
		return NULL;
	}

	newnode=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));
	if (!newnode)
	{
		printf("%s","Failed to allocate memory for new node!\n");
		rat_dealloc((void *)newseg1);
		return NULL;
	}

	newseg0=seg;

	memset(newseg1,0,sizeof(SSegment));
	memset(newnode,0,sizeof(SNode));

	game->nodes=(SNode **)rat_realloc((void *)game->nodes,sizeof(SNode *)*(++game->num_nodes));
	game->nodes[game->num_nodes-1]=newnode;

	game->segs=(SSegment **)rat_realloc((void *)game->segs,sizeof(SSegment *)*(++game->num_segs));
	game->segs[game->num_segs-1]=newseg1;

	newseg0->node0=node0;
	newseg0->node1=newnode;
	newseg0->orientation=seg->orientation;

	newseg1->node0=node1;
	newseg1->node1=newnode;
	newseg1->orientation=seg->orientation;

	newnode->x=newx;
	newnode->y=newy;
	newnode->seg0=newseg0;
	newnode->seg1=newseg1;

	if (node_links&NODE0_IS_0)
		node0->seg0=newseg0;
	else
		node0->seg1=newseg0;

	if (node_links&NODE1_IS_0)
		node1->seg0=newseg1;
	else
		node1->seg1=newseg1;

	return newnode;
}

int check_for_lateral_movement(SGame *game,unsigned flag0,unsigned flag1)
{
	int lateral_movement=0;

	if (game->flags&flag0) lateral_movement-=1;
	if (game->flags&flag1) lateral_movement+=1;

	if (lateral_movement)
	{
		const int result=start_draw(game,lateral_movement);
		if (result<0)
		{
			printf("%s","Failed to divide segment!\n");
			return 1;
		}
		else if (result>0)
		{
			printf("%s","start_draw called at invalid time.\n");
			return 1;
		}
	}

	return 0;
}

int line_walk_control(SGame *game,unsigned char *keys)
{
	// If we're travelling freely along a line:

	// first we move the character directly based on user
	// input and orientation of the line we're on.  then check
	// field boundaries, which is especially important
	// because we are using the entire range of the
	// unsigned short.  then if we're close enough to a
	// node, we'll interpret lateral movement as a jump
	// on to the other line on the node.  otherwise we'll
	// interpret lateral movement as the the cue for
	// drawing new sections.  Also make sure you don't
	// run off the end.

	if (game->current_seg->orientation&ORIENTATION_VERT)
	{
		SNode *low_node,*high_node;

		if (game->flags&GAME_FLAG_INPUT_UP) game->velocity-=PLAYER_SPEED;
		if (game->flags&GAME_FLAG_INPUT_DOWN) game->velocity+=PLAYER_SPEED;

		game->position_x=game->current_node->x;
		if ((game->position_y<PLAYER_SPEED)&&(game->velocity==-PLAYER_SPEED))
			game->position_y=0;
		else if ((game->position_y>HEIGHT_MAX-PLAYER_SPEED)&&(game->velocity==PLAYER_SPEED))
			game->position_y=HEIGHT_MAX;
		else
			game->position_y+=game->velocity;

		if (game->current_seg->node0->y<game->current_seg->node1->y)
		{
			low_node=game->current_seg->node0;
			high_node=game->current_seg->node1;
		}
		else
		{
			low_node=game->current_seg->node1;
			high_node=game->current_seg->node0;
		}

		if (game->position_y>high_node->y||game->position_y<low_node->y)
		{
			if (game->position_y>high_node->y) game->position_y=high_node->y;
			if (game->position_y<low_node->y) game->position_y=low_node->y;
		}

		if ((game->position_y<low_node->y+PLAYER_NODE_JUMP_THRESHOLD)&&
			(high_node->y-PLAYER_NODE_JUMP_THRESHOLD<game->position_y))
		{
			if (game->position_y-low_node->y<high_node->y-game->position_y)
			{
				game->current_node=low_node;
				if ((game->flags&GAME_FLAG_INPUT_LEFT)||(game->flags&GAME_FLAG_INPUT_RIGHT))
				{
					game->current_seg=game->current_seg==game->current_node->seg0?
						game->current_node->seg1:game->current_node->seg0;
				}
			}
			else
			{
				game->current_node=high_node;
				if ((game->flags&GAME_FLAG_INPUT_LEFT)||(game->flags&GAME_FLAG_INPUT_RIGHT))
				{
					game->current_seg=game->current_seg==game->current_node->seg0?
						game->current_node->seg1:game->current_node->seg0;
				}
			}
		}
		else if (game->position_y<low_node->y+PLAYER_NODE_JUMP_THRESHOLD)
		{
			game->current_node=low_node;
			if ((game->flags&GAME_FLAG_INPUT_LEFT)||(game->flags&GAME_FLAG_INPUT_RIGHT))
			{
				game->current_seg=game->current_seg==game->current_node->seg0?
					game->current_node->seg1:game->current_node->seg0;
			}
		}
		else if (high_node->y-PLAYER_NODE_JUMP_THRESHOLD<game->position_y)
		{
			game->current_node=high_node;
			if ((game->flags&GAME_FLAG_INPUT_LEFT)||(game->flags&GAME_FLAG_INPUT_RIGHT))
			{
				game->current_seg=game->current_seg==game->current_node->seg0?
					game->current_node->seg1:game->current_node->seg0;
			}
		}
		else
		{
			return check_for_lateral_movement(game,GAME_FLAG_INPUT_LEFT,GAME_FLAG_INPUT_RIGHT);
		}
	}
	else // if the segment is horizontal
	{
		SNode *low_node,*high_node;

		if (game->flags&GAME_FLAG_INPUT_LEFT) game->velocity-=PLAYER_SPEED;
		if (game->flags&GAME_FLAG_INPUT_RIGHT) game->velocity+=PLAYER_SPEED;

		game->position_y=game->current_node->y;
		if ((game->position_x<PLAYER_SPEED)&&(game->velocity==-PLAYER_SPEED))
			game->position_x=0;
		else if ((game->position_x>WIDTH_MAX-PLAYER_SPEED)&&(game->velocity==PLAYER_SPEED))
			game->position_x=WIDTH_MAX;
		else
			game->position_x+=game->velocity;

		if (game->current_seg->node0->x<game->current_seg->node1->x)
		{
			low_node=game->current_seg->node0;
			high_node=game->current_seg->node1;
		}
		else
		{
			low_node=game->current_seg->node1;
			high_node=game->current_seg->node0;
		}

		if (game->position_x>high_node->x||game->position_x<low_node->x)
		{
			if (game->position_x>high_node->x) game->position_x=high_node->x;
			if (game->position_x<low_node->x) game->position_x=low_node->x;
		}

		if (game->position_x>high_node->x) game->position_x=high_node->x;
		if (game->position_x<low_node->x) game->position_x=low_node->x;

		if ((game->position_x<low_node->y+PLAYER_NODE_JUMP_THRESHOLD)&&
			(high_node->x-PLAYER_NODE_JUMP_THRESHOLD<game->position_x))
		{
			if (game->position_x-low_node->x<high_node->x-game->position_x)
			{
				game->current_node=low_node;
				if ((game->flags&GAME_FLAG_INPUT_UP)||(game->flags&GAME_FLAG_INPUT_DOWN))
				{
					game->current_seg=game->current_seg==game->current_node->seg0?
						game->current_node->seg1:game->current_node->seg0;
				}
			}
			else
			{
				game->current_node=high_node;
				if ((game->flags&GAME_FLAG_INPUT_UP)||(game->flags&GAME_FLAG_INPUT_DOWN))
				{
					game->current_seg=game->current_seg==game->current_node->seg0?
						game->current_node->seg1:game->current_node->seg0;
				}
			}
		}
		else if (game->position_x<low_node->x+PLAYER_NODE_JUMP_THRESHOLD)
		{
			game->current_node=low_node;
			if ((game->flags&GAME_FLAG_INPUT_UP)||(game->flags&GAME_FLAG_INPUT_DOWN))
			{
				game->current_seg=game->current_seg==game->current_node->seg0?
					game->current_node->seg1:game->current_node->seg0;
			}
		}
		else if (high_node->x-PLAYER_NODE_JUMP_THRESHOLD<game->position_x)
		{
			game->current_node=high_node;
			if ((game->flags&GAME_FLAG_INPUT_UP)||(game->flags&GAME_FLAG_INPUT_DOWN))
			{
				game->current_seg=game->current_seg==game->current_node->seg0?
					game->current_node->seg1:game->current_node->seg0;
			}
		}
		else
		{
			return check_for_lateral_movement(game,GAME_FLAG_INPUT_UP,GAME_FLAG_INPUT_DOWN);
		}
	}

	return 0;
}

__inline static int append_node_to_line(SGame *game)
{
	game->num_draw_segs++;
	game->num_draw_nodes++;

	game->draw_segs=(SSegment **)rat_realloc((void *)game->draw_segs,sizeof(SSegment *)*game->num_draw_segs);
	if (!game->draw_segs)
	{
		printf("Failed to reallocate memory for segments list!\n");
		return 1;
	}

	game->draw_nodes=(SNode **)rat_realloc((void *)game->draw_nodes,sizeof(SNode *)*game->num_draw_nodes);
	if (!game->draw_nodes)
	{
		printf("Failed to reallocate memory for nodes list!\n");
		return 1;
	}

	game->draw_segs[game->num_draw_segs-1]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
	game->draw_nodes[game->num_draw_nodes-1]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));

	game->draw_segs[game->num_draw_segs-1]->node0=game->draw_nodes[game->num_draw_nodes-2];
	game->draw_segs[game->num_draw_segs-1]->node1=game->draw_nodes[game->num_draw_nodes-1];

	if (game->draw_nodes[game->num_draw_nodes-2]->seg1==NULL)
		game->draw_nodes[game->num_draw_nodes-2]->seg1=game->draw_segs[game->num_draw_segs-1];
	else
		game->draw_nodes[game->num_draw_nodes-2]->seg0=game->draw_segs[game->num_draw_segs-1];

	game->draw_nodes[game->num_draw_nodes-1]->seg0=game->draw_segs[game->num_draw_segs-1];
	game->draw_nodes[game->num_draw_nodes-1]->seg1=NULL;

	game->draw_nodes[game->num_draw_nodes-1]->x=game->position_x;
	game->draw_nodes[game->num_draw_nodes-1]->y=game->position_y;

	return 0;
}

// check to see if a point is inside the loop using
// the quadrant crossings counter method
__inline static int is_point_inside_loop(SGame *game,SNode *start_node,unsigned short ptx,unsigned short pty)
{
	SNode *last_node=start_node;
	SSegment *this_seg=start_node->seg0;

	char kickout=0;
	short crossings_counter=0;
	char crossing_direction=0;

	for (;;)
	{
		SNode *next_node=this_seg->node0==last_node?this_seg->node1:this_seg->node0;

		if ((this_seg==start_node->seg0)&&kickout) break;
		kickout=-1;

		// check for a crossing
		crossing_direction=0;
		if (this_seg->orientation&ORIENTATION_VERT)
		{
			if ((last_node->y>pty)&&(next_node->y<pty))
			{
				if (last_node->x<ptx)
					crossing_direction=1;
				else if (last_node->x>ptx)
					crossing_direction=-1;
			}
			else if ((last_node->y<pty)&&(next_node->y>pty))
			{
				if (last_node->x<ptx)
					crossing_direction=-1;
				else if (last_node->x>ptx)
					crossing_direction=1;
			}
		}
		else // if orientation is horizontal
		{
			if ((last_node->x>ptx)&&(next_node->x<ptx))
			{
				if (last_node->y<pty)
					crossing_direction=-1;
				else if (last_node->y>pty)
					crossing_direction=1;
			}
			else if ((last_node->x<ptx)&&(next_node->x>ptx))
			{
				if (last_node->y<pty)
					crossing_direction=1;
				else if (last_node->y>pty)
					crossing_direction=-1;
			}
		}

		crossings_counter+=crossing_direction;

		this_seg=next_node->seg0==this_seg?next_node->seg1:next_node->seg0;
		last_node=next_node;
	}

	return crossings_counter;
}

// link the new inside loop based on which of the two
// possibilities contains the ray
void link_new_loop(SGame *game,SNode *endnode0,SNode *endnode1,SSegment *last_seg)
{
	// store the original linkage of the end nodes
	SNode endnode0_old,endnode1_old;

	register unsigned int i;
	int crossings_counter;

	memcpy(&endnode0_old,endnode0,sizeof(SNode));
	memcpy(&endnode1_old,endnode1,sizeof(SNode));

	// try linking the loop one way, then test if the
	// electric ray is inside
	endnode0->seg0=game->draw_segs[0];
	game->draw_segs[game->num_draw_segs-1]->node1=endnode1;
	if (endnode1->seg0==last_seg)
		endnode1->seg1=game->draw_segs[game->num_draw_segs-1];
	else
		endnode1->seg0=game->draw_segs[game->num_draw_segs-1];

	for (i=0; i<game->num_rays; i++)
	{
		SBodyBin *bodybin=game->rays[i].bodies;

		for (; bodybin; bodybin=bodybin->next)
		{
			if (bodybin->body)
			{
				vector2 pos=rat_hull_get_pos(bodybin->body->hull);

				// if the ray is inside the loop, return because we're good to go
				if (abs(is_point_inside_loop(game,endnode0,
					(unsigned short)(pos.x*((float)WIDTH_MAX/700.0f)),
					(unsigned short)(pos.y*((float)HEIGHT_MAX/500.0f))))==4)
					return;
			}
		}
	}

	// otherwise restore original unlooped state
	memcpy(endnode0,&endnode0_old,sizeof(SNode));
	memcpy(endnode1,&endnode1_old,sizeof(SNode));

	// link the end nodes to loop the other way!
	endnode0->seg1=game->draw_segs[0];
	game->draw_segs[game->num_draw_segs-1]->node1=endnode1;
	if (endnode1->seg0==last_seg)
		endnode1->seg0=game->draw_segs[game->num_draw_segs-1];
	else
		endnode1->seg1=game->draw_segs[game->num_draw_segs-1];
}

int finish_draw(SGame *game,char orientation,SSegment *collision_seg)
{
	register unsigned int i;

	SSegment *last_seg;
	SNode *this_node;

	SNode *endnode0,*endnode1;

	if (orientation&ORIENTATION_VERT)
		game->position_y=collision_seg->node0->y;
	else
		game->position_x=collision_seg->node0->x;

	endnode0=game->current_node;
	endnode1=divide_segment(game,collision_seg,game->position_x,game->position_y);

	this_node=endnode0;
	last_seg=endnode0->seg0;

	for (;;)
	{
		SSegment *next_seg;

		next_seg=this_node->seg0==last_seg?this_node->seg1:this_node->seg0;
		this_node=next_seg->node0==this_node?next_seg->node1:next_seg->node0;
		last_seg=next_seg;

		if (this_node==endnode1)
		{
			unsigned long new_num_segs=game->num_draw_segs+game->num_segs;
			unsigned long new_num_nodes=game->num_draw_nodes+game->num_nodes-1;

			link_new_loop(game,endnode0,endnode1,last_seg);

			rat_block_dealloc(game->bal,(void *)game->draw_nodes[game->num_draw_nodes-1],sizeof(SNode));

			game->nodes=(SNode **)rat_realloc((void *)game->nodes,sizeof(SNode *)*new_num_nodes);
			game->segs=(SSegment **)rat_realloc((void *)game->segs,sizeof(SSegment *)*new_num_segs);

			memmove(game->nodes+game->num_nodes,game->draw_nodes,sizeof(SNode *)*(game->num_draw_nodes-1));
			memmove(game->segs+game->num_segs,game->draw_segs,sizeof(SSegment *)*game->num_draw_segs);

			game->num_nodes=new_num_nodes;
			game->num_segs=new_num_segs;

			game->num_draw_nodes=0;
			game->num_draw_segs=0;

			game->current_node=endnode1;
			game->current_seg=endnode1->seg0;

			break;
		}
	}

	game->play_state=PlayState_LineWalk;

	// we're done with the draw seg lists
	rat_dealloc((void *)game->draw_segs);
	rat_dealloc((void *)game->draw_nodes);

	tesselate_main_loop(game);

	return 0;
}

int draw_advance(SGame *game,char orientation,unsigned short newdim)
{
	char kickout=0;

	SSegment *this_seg=game->current_seg;
	SNode *last_node=game->current_seg->node0;

	// in case we intersect
	SSegment *collision_seg;

	if (orientation&ORIENTATION_VERT)
	{
		char intersected=0;

		// check for intersection with the main loop
		for (;;)
		{
			SNode *next_node;

			// check for an intersection
			if (this_seg->orientation&ORIENTATION_HORZ)
			{
				unsigned short lowx,highx;

				if (this_seg->node0->x<this_seg->node1->x)
				{
					lowx=this_seg->node0->x;
					highx=this_seg->node1->x;
				}
				else
				{
					lowx=this_seg->node1->x;
					highx=this_seg->node0->x;
				}

				if ((lowx<=game->position_x)&&(highx>=game->position_x))
				{
					if (((game->position_y<=this_seg->node0->y)&&
						(newdim>=this_seg->node0->y))||
						((game->position_y>=this_seg->node0->y)&&
						(newdim<=this_seg->node0->y)))
					{
						intersected=-1;
						collision_seg=this_seg;
						break;
					}
				}
			}

			next_node=this_seg->node0==last_node?this_seg->node1:this_seg->node0;
			this_seg=next_node->seg0==this_seg?next_node->seg1:next_node->seg0;
			last_node=next_node;

			if ((this_seg==game->current_seg)&&kickout) break;
			kickout=-1;
		}

		if (intersected)
			return finish_draw(game,orientation,collision_seg);
		else
			game->position_y=newdim;
	}
	else // if the orientation is horizontal
	{
		char intersected=0;

		// check for intersection with the main loop
		for (;;)
		{
			SNode *next_node;

			// check for an intersection
			if (this_seg->orientation&ORIENTATION_VERT)
			{
				unsigned short lowy,highy;

				if (this_seg->node0->y<this_seg->node1->y)
				{
					lowy=this_seg->node0->y;
					highy=this_seg->node1->y;
				}
				else
				{
					lowy=this_seg->node1->y;
					highy=this_seg->node0->y;
				}

				if ((lowy<=game->position_y)&&(highy>=game->position_y))
				{
					if (((game->position_x<=this_seg->node0->x)&&
						(newdim>=this_seg->node0->x))||
						((game->position_x>=this_seg->node0->x)&&
						(newdim<=this_seg->node0->x)))
					{
						intersected=-1;
						collision_seg=this_seg;
						break;
					}
				}
			}

			next_node=this_seg->node0==last_node?this_seg->node1:this_seg->node0;
			this_seg=next_node->seg0==this_seg?next_node->seg1:next_node->seg0;
			last_node=next_node;

			if ((this_seg==game->current_seg)&&kickout) break;
			kickout=-1;
		}

		if (intersected)
			return finish_draw(game,orientation,collision_seg);
		else
			game->position_x=newdim;
	}

	return 0;
}

int draw_collide(SGame *game,char orientation)
{
	register unsigned int i;
	char blocked=0;

	if (orientation&ORIENTATION_VERT)
	{
		unsigned short newy;

		if ((game->position_y<PLAYER_SPEED)&&(game->velocity==-PLAYER_SPEED))
			game->velocity=-game->position_y;
		else if ((game->position_y>HEIGHT_MAX-PLAYER_SPEED)&&(game->velocity==PLAYER_SPEED))
			game->velocity=HEIGHT_MAX-game->position_y;

		newy=game->position_y+game->velocity;

		if (game->num_draw_segs>1)
		{
			for (i=0; i<game->num_draw_segs-2; i++)
			{
				if (game->draw_segs[i]->orientation&ORIENTATION_HORZ)
				{
					unsigned short lowx,highx;

					if (game->draw_segs[i]->node0->x<game->draw_segs[i]->node1->x)
					{
						lowx=game->draw_segs[i]->node0->x;
						highx=game->draw_segs[i]->node1->x;
					}
					else
					{
						lowx=game->draw_segs[i]->node1->x;
						highx=game->draw_segs[i]->node0->x;
					}

					if ((lowx<=game->position_x)&&(highx>=game->position_x))
					{
						if (((game->position_y<=game->draw_segs[i]->node0->y)&&
							(newy>=game->draw_segs[i]->node0->y))||
							((game->position_y>=game->draw_segs[i]->node0->y)&&
							(newy<=game->draw_segs[i]->node0->y)))
						{
							blocked=-1;
							break;
						}
					}
				}
			}
		}

		if (!blocked)
			return draw_advance(game,ORIENTATION_VERT,newy);
	}
	else
	{
		unsigned short newx;

		if ((game->position_x<PLAYER_SPEED)&&(game->velocity==-PLAYER_SPEED))
			game->velocity=-game->position_x;
		else if ((game->position_x>WIDTH_MAX-PLAYER_SPEED)&&(game->velocity==PLAYER_SPEED))
			game->velocity=WIDTH_MAX-game->position_x;

		newx=game->position_x+game->velocity;

		if (game->num_draw_segs>1)
		{
			for (i=0; i<game->num_draw_segs-2; i++)
			{
				if (game->draw_segs[i]->orientation&ORIENTATION_VERT)
				{
					unsigned short lowy,highy;

					if (game->draw_segs[i]->node0->y<game->draw_segs[i]->node1->y)
					{
						lowy=game->draw_segs[i]->node0->y;
						highy=game->draw_segs[i]->node1->y;
					}
					else
					{
						lowy=game->draw_segs[i]->node1->y;
						highy=game->draw_segs[i]->node0->y;
					}

					if ((lowy<=game->position_y)&&(highy>=game->position_y))
					{
						if (((game->position_x<=game->draw_segs[i]->node0->x)&&
							(newx>=game->draw_segs[i]->node0->x))||
							((game->position_x>=game->draw_segs[i]->node0->x)&&
							(newx<=game->draw_segs[i]->node0->x)))
						{
							blocked=-1;
							break;
						}
					}
				}
			}
		}

		if (!blocked)
			return draw_advance(game,ORIENTATION_HORZ,newx);
	}

	return 0;
}

int draw_area_control(SGame *game,unsigned char *keys)
{
	// If we're drawing new area:
	//
	// Draw along current axis ONLY if the direction
	// set is pressed, and only in that direction.  If
	// you move laterally, we break the segment with a
	// new node, set the new axis and direction and
	// continue.  Check for collision against new drawn
	// segments and block the player from passing through,
	// then check for collision against the main loop
	// to tell when they're done drawing and where they
	// hit.

	if (game->draw_segs[game->num_draw_segs-1]->orientation&ORIENTATION_VERT)
	{
		if (game->draw_direction>0)
		{
			if (game->flags&GAME_FLAG_INPUT_DOWN) game->velocity=PLAYER_SPEED;
			if (game->flags&GAME_FLAG_INPUT_UP) game->velocity=0;
		}
		else
		{
			if (game->flags&GAME_FLAG_INPUT_UP) game->velocity=-PLAYER_SPEED;
			if (game->flags&GAME_FLAG_INPUT_DOWN) game->velocity=0;
		}
		if (game->velocity)
		{
			draw_collide(game,ORIENTATION_VERT);
			game->velocity=0;
		}
		else
		{
			if (game->flags&GAME_FLAG_INPUT_LEFT)
			{
				game->draw_direction=DIRECTION_NEG;
				if (append_node_to_line(game)) return 1;

				game->draw_segs[game->num_draw_segs-1]->orientation=ORIENTATION_HORZ;
			}
			else if (game->flags&GAME_FLAG_INPUT_RIGHT)
			{
				game->draw_direction=DIRECTION_POS;
				if (append_node_to_line(game)) return 1;

				game->draw_segs[game->num_draw_segs-1]->orientation=ORIENTATION_HORZ;
			}
		}
	}
	else // if the segment is horizontal
	{
		if (game->draw_direction>0)
		{
			if (game->flags&GAME_FLAG_INPUT_RIGHT) game->velocity=PLAYER_SPEED;
			if (game->flags&GAME_FLAG_INPUT_LEFT) game->velocity=0;
		}
		else
		{
			if (game->flags&GAME_FLAG_INPUT_LEFT) game->velocity=-PLAYER_SPEED;
			if (game->flags&GAME_FLAG_INPUT_RIGHT) game->velocity=0;
		}
		if (game->velocity)
		{
			draw_collide(game,ORIENTATION_HORZ);
			game->velocity=0;
		}
		else
		{
			if (game->flags&GAME_FLAG_INPUT_UP)
			{
				game->draw_direction=DIRECTION_NEG;
				if (append_node_to_line(game)) return 1;

				game->draw_segs[game->num_draw_segs-1]->orientation=ORIENTATION_VERT;
			}
			else if (game->flags&GAME_FLAG_INPUT_DOWN)
			{
				game->draw_direction=DIRECTION_POS;
				if (append_node_to_line(game)) return 1;

				game->draw_segs[game->num_draw_segs-1]->orientation=ORIENTATION_VERT;
			}
		}
	}

	if (game->play_state==PlayState_DrawArea)
	{
		game->draw_nodes[game->num_draw_nodes-1]->x=game->position_x;
		game->draw_nodes[game->num_draw_nodes-1]->y=game->position_y;
	}

	return 0;
}

// get input and control the player character.
int control_player(SGame *game,unsigned char *keys)
{
	game->velocity=0;
	if (game->play_state==PlayState_LineWalk)
		return line_walk_control(game,keys);
	else if (game->play_state==PlayState_DrawArea)
		return draw_area_control(game,keys);
	else
		return 1;
}

void update_rays(SGame *game)
{
	register unsigned int i,j,k,dir;

	rat_real products[4];

	const vector2 directions[4]=
	{
		{ 1, 0},
		{ 0, 1},
		{-1, 0},
		{ 0,-1},
	};

	if (clock()-game->last_time_update>THRUST_UPDATE_DELAY*CLOCKS_PER_SEC)
	{
		game->last_time_update=clock();

		for (i=0; i<game->num_rays; i++)
		{
			rat_real thrust_angle=rat_hull_get_angle(game->rays[i].bodies->body->hull);
			vector2 thrust_pos=
				vector2_add(
					rat_hull_get_pos(game->rays[i].bodies->body->hull),
					vector2_getrotated(game->rays[i].thruster.offset,thrust_angle));

			vector2 player_pos=
			{
				((rat_real)game->position_x/(rat_real)WIDTH_MAX)*700.0f,
				((rat_real)game->position_y/(rat_real)HEIGHT_MAX)*500.0f
			};

			vector2 local_pos=vector2_world_to_local(player_pos,thrust_pos,thrust_angle);

			vector2 thrust=local_pos;

			for (j=0; j<4; j++)
				products[j]=vector2_dot(directions[j],thrust);

			dir=0;
			for (j=1; j<4; j++)
			{
				if (products[j]>products[dir]) dir=j;
			}

			game->rays->thruster.thrust=directions[dir];
		}
	}

	for (i=0; i<game->num_rays; i++)
	{
		vector2 xformed_thrust=vector2_getrotated
			(vector2_multf(game->rays->thruster.thrust,0.25f),
			rat_hull_get_angle(game->rays->bodies->body->hull));
		vector2 xformed_offset=vector2_getrotated
			(vector2_negative(game->rays->thruster.offset),
			-(MATH_PI-rat_hull_get_angle(game->rays->bodies->body->hull)));

		rat_body_apply_offset_impulse(game->rays[i].bodies->body,xformed_thrust,xformed_offset);
		rat_body_set_is_asleep(game->rays[i].bodies->body,0);
	}

	for (i=0; i<game->num_draw_segs; i++)
	{
		vector2 verts[2]=
		{
			{game->draw_segs[i]->node0->x/(double)WIDTH_MAX*700.0,
			 game->draw_segs[i]->node0->y/(double)HEIGHT_MAX*500.0},

			{game->draw_segs[i]->node1->x/(double)WIDTH_MAX*700.0,
			 game->draw_segs[i]->node1->y/(double)HEIGHT_MAX*500.0}
		};

		rat_hull *seghull=rat_hull_polygon_create(null_vec,2,verts);

		for (j=0; j<game->num_rays; j++)
		{
			if (rat_query_collision(NULL,seghull,game->rays[j].bodies->body->hull,NULL))
			{
				game->game_state=GameState_GameOver;
			}
		}

		rat_hull_destroy(seghull);
	}
}

// this is the master game logic function.  it calls it's
// various workhorses and does game timing
int update_game(SGame *game,unsigned char *keys)
{
	// a counter that will be used to determine when
	// we should clean up the short block manager
	static unsigned int cleanctr=0;

	// used in our timing code
	static clock_t oldtime=0;
	static clock_t newtime=0;

	static double timeacc=0;

	int result=0;

	if ((oldtime!=0)&&(newtime!=0))
	{
		int ctr=0;
		newtime=clock();
		timeacc+=((double)(newtime-oldtime)/(double)CLOCKS_PER_SEC);
		oldtime=newtime;

		while (timeacc>=0.006)
		{
			if (ctr++>6) break;
			timeacc-=0.006;

			result=control_player(game,keys);
			if (result) return result;

			update_rays(game);

			// every sixteen iterations it cleans the block physics manager.
			rat_world_update(game->phys_world,0.02f,(cleanctr=++cleanctr%16)==0);

			if (game->game_state==GameState_GameOver) game->game_state=GameState_EndGame;
		}
		timeacc=0;
	}
	else
	{
		oldtime=clock();
		newtime=clock();
	}

	return 0;
}

int initialize_draw_data(SGame *game)
{
	game->num_draw_segs=1;
	game->num_draw_nodes=1;

	game->draw_segs=NULL;
	game->draw_segs=(SSegment **)rat_alloc(sizeof(SSegment *));
	if (!game->draw_segs)
	{
		printf("Failed to allocate memory for segments list!\n");
		return 1;
	}

	game->draw_nodes=NULL;
	game->draw_nodes=(SNode **)rat_alloc(sizeof(SNode *));
	if (!game->draw_nodes)
	{
		printf("Failed to allocate memory for nodes list!\n");
		return 1;
	}

	return 0;
}

__inline static int is_valid_draw_down(SGame *game)
{
	return abs(is_point_inside_loop(game,game->current_node,game->position_x,game->position_y+1))==4;
}

__inline static int is_valid_draw_up(SGame *game)
{
	return abs(is_point_inside_loop(game,game->current_node,game->position_x,game->position_y-1))==4;
}

__inline static int is_valid_draw_right(SGame *game)
{
	return abs(is_point_inside_loop(game,game->current_node,game->position_x+1,game->position_y))==4;
}

__inline static int is_valid_draw_left(SGame *game)
{
	return abs(is_point_inside_loop(game,game->current_node,game->position_x-1,game->position_y))==4;
}

// if the player wants to start drawing a new loop,
// this initializes that process.
int start_draw(SGame *game,int lateral_movement)
{
	if (lateral_movement>0)
	{
		if (game->current_seg->orientation&ORIENTATION_HORZ)
		{
			if (game->position_y>HEIGHT_MAX-PLAYER_SPEED)
				return 0; // no room to draw
			else if (is_valid_draw_down(game))
			{
				SNode *newnode=divide_segment(game,game->current_seg,game->position_x,game->position_y);
				if (!newnode) return -1;

				game->current_node=newnode;
				game->current_seg=newnode->seg1; // does not matter which, since we're changing modes

				game->play_state=PlayState_DrawArea;
				game->position_y+=PLAYER_SPEED;
				game->draw_direction=DIRECTION_POS;

				if (initialize_draw_data(game)) return 1;

				game->draw_segs[0]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
				game->draw_nodes[0]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));

				game->draw_segs[0]->node0=newnode;
				game->draw_segs[0]->node1=game->draw_nodes[0];
				game->draw_segs[0]->orientation=ORIENTATION_VERT;

				game->draw_nodes[0]->seg0=game->draw_segs[0];
				game->draw_nodes[0]->seg1=NULL;
				game->draw_nodes[0]->x=game->position_x;
				game->draw_nodes[0]->y=game->position_y;

				return 0;
			}
		}
		else
		{
			if (game->position_x>WIDTH_MAX-PLAYER_SPEED)
				return 0; // no room to draw
			else if (is_valid_draw_right(game))
			{
				SNode *newnode=divide_segment(game,game->current_seg,game->position_x,game->position_y);
				if (!newnode) return -1;

				game->current_node=newnode;
				game->current_seg=newnode->seg1; // does not matter which, since we're changing modes

				game->play_state=PlayState_DrawArea;
				game->position_x+=PLAYER_SPEED;
				game->draw_direction=DIRECTION_POS;

				if (initialize_draw_data(game)) return 1;

				game->draw_segs[0]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
				game->draw_nodes[0]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));

				game->draw_segs[0]->node0=newnode;
				game->draw_segs[0]->node1=game->draw_nodes[0];
				game->draw_segs[0]->orientation=ORIENTATION_HORZ;

				game->draw_nodes[0]->seg0=game->draw_segs[0];
				game->draw_nodes[0]->seg1=NULL;
				game->draw_nodes[0]->x=game->position_x;
				game->draw_nodes[0]->y=game->position_y;

				return 0;
			}
		}
		return 0;
	}
	else if (lateral_movement<0)
	{
		if (game->current_seg->orientation&ORIENTATION_HORZ)
		{
			if (game->position_y<PLAYER_SPEED)
				return 0; // no room to draw
			else  if (is_valid_draw_up(game))
			{
				SNode *newnode=divide_segment(game,game->current_seg,game->position_x,game->position_y);
				if (!newnode) return -1;

				game->current_node=newnode;
				game->current_seg=newnode->seg1; // does not matter which, since we're changing modes

				game->play_state=PlayState_DrawArea;
				game->position_y-=PLAYER_SPEED;
				game->draw_direction=DIRECTION_NEG;

				if (initialize_draw_data(game)) return 1;

				game->draw_segs[0]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
				game->draw_nodes[0]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));

				game->draw_segs[0]->node0=newnode;
				game->draw_segs[0]->node1=game->draw_nodes[0];
				game->draw_segs[0]->orientation=ORIENTATION_VERT;

				game->draw_nodes[0]->seg0=game->draw_segs[0];
				game->draw_nodes[0]->seg1=NULL;
				game->draw_nodes[0]->x=game->position_x;
				game->draw_nodes[0]->y=game->position_y;

				return 0;
			}
		}
		else
		{
			if (game->position_x<PLAYER_SPEED)
				return 0; // no room to draw
			else if (is_valid_draw_left(game))
			{
				SNode *newnode=divide_segment(game,game->current_seg,game->position_x,game->position_y);
				if (!newnode) return -1;

				game->current_node=newnode;
				game->current_seg=newnode->seg1; // does not matter which, since we're changing modes

				game->play_state=PlayState_DrawArea;
				game->position_x-=PLAYER_SPEED;
				game->draw_direction=DIRECTION_NEG;

				if (initialize_draw_data(game)) return 1;

				game->draw_segs[0]=(SSegment *)rat_block_alloc(game->bal,sizeof(SSegment));
				game->draw_nodes[0]=(SNode *)rat_block_alloc(game->bal,sizeof(SNode));

				game->draw_segs[0]->node0=newnode;
				game->draw_segs[0]->node1=game->draw_nodes[0];
				game->draw_segs[0]->orientation=ORIENTATION_HORZ;

				game->draw_nodes[0]->seg0=game->draw_segs[0];
				game->draw_nodes[0]->seg1=NULL;
				game->draw_nodes[0]->x=game->position_x;
				game->draw_nodes[0]->y=game->position_y;

				return 0;
			}
		}
		return 0;
	}
	else
	{
		// no lateral movement!  how the hell did this even get called?
		printf("%s%s",
			"Area draw game_state tried to initialize, even though\n",
			"   there was no lateral movement in the midst of a segment!\n");

		return 1;
	}
}

// qsort callback to compare two ushorts
int compare_ushort(const void *a, const void *b)
{
	if (*(unsigned short *)a<*(unsigned short *)b) return -1;
	if (*(unsigned short *)a>*(unsigned short *)b) return 1;

	return 0;
}

__inline static rat_body *create_box(vector2 pos,vector2 size)
{
	vector2 verts[4];

	verts[0].x=0;		verts[0].y=0;
	verts[1].x=0;		verts[1].y=size.y;
	verts[2].x=size.x;	verts[2].y=size.y;
	verts[3].x=size.x;	verts[3].y=0;

	return rat_body_create(
		rat_hull_polygon_create(pos,4,verts),
		1.0f,0.0f,0.0f,0.0f);
}

__inline static int check_right_bound(char *contain_table,unsigned int pitch,
									  unsigned int y0,unsigned int y1,unsigned int x)
{
	register unsigned int i;

	for (i=y0; i<=y1; i++)
	{
		if (contain_table[x+1+i*pitch]) return 0;
	}
	return 1;
}

__inline static int check_bottom_bound(char *contain_table,unsigned int pitch,
									   unsigned int x0,unsigned int x1,unsigned int y)
{
	register unsigned int i;

	for (i=x0; i<=x1; i++)
	{
		if (contain_table[i+(y+1)*pitch]) return 0;
	}
	return 1;
}

static void inflate_tesselate(SGame *game,unsigned int numx,unsigned int numy,
	unsigned short *xdims,unsigned short *ydims,const char *_contain_table)
{
	register unsigned int i,j,k,l;
	register int grow_x,grow_y;
	unsigned int x0=0,y0=0,x1=0,y1=0;
	char *contain_table;

	vector2 pos,size;

	contain_table=(char *)rat_alloc(numx*numy);
	memcpy(contain_table,_contain_table,numx*numy);

	for (j=0; j<numy-1; j++)
	{
		for (i=0; i<numx-1; i++)
		{
			if (!contain_table[i+j*numx])
			{
				grow_x=grow_y=-1;
				x0=x1=i;
				y0=y1=j;

				while (grow_x||grow_y)
				{
					if (grow_x&&x1<numx-1)
					{
						if (!check_right_bound(contain_table,numx,y0,y1,x1))
							grow_x=0;
						else
							x1++;
					}
					else
						grow_x=0;

					if (grow_y&&y1<numy-1)
					{
						if (!check_bottom_bound(contain_table,numx,x0,x1,y1))
							grow_y=0;
						else
							y1++;
					}
					else
						grow_y=0;
				}

				for (k=y0; k<y1+1; k++)
				{
					for (l=x0; l<x1+1; l++)
						contain_table[l+numx*k]=-1;
				}

				i=x1;

				// actually put this shit together.
				pos.x=(double)xdims[x0]/(double)WIDTH_MAX*700.0;
				pos.y=(double)ydims[y0]/(double)HEIGHT_MAX*500.0;

				size.x=((double)xdims[x1+1]/(double)WIDTH_MAX*700.0)-pos.x;
				size.y=((double)ydims[y1+1]/(double)HEIGHT_MAX*500.0)-pos.y;

				if (game->wall_quads)
					game->wall_quads=(rat_body **)rat_realloc
						((void *)game->wall_quads,sizeof(rat_body *)*++game->num_wall_quads);
				else
				{
					game->wall_quads=(rat_body **)rat_alloc(sizeof(rat_body *));
					game->num_wall_quads=1;
				}

				rat_world_add_body(game->phys_world,
					game->wall_quads[game->num_wall_quads-1]=create_box(pos,size));
			}
		}
	}

	rat_dealloc((void *)contain_table);
}

// tesselate the main loop enclosed area in to
// quadrilaterals
void tesselate_main_loop(SGame *game)
{
	register unsigned int i,j/*,i2*/,j2,quadctr=0;
	unsigned int num_dims=3,numx=3,numy=3;
	double capture_percent,tempdbl;
	unsigned short *xdims,*ydims;
	char *contain_table,kickout=0;

	SSegment *last_seg=game->current_node->seg0;
	SNode *this_node=game->current_node;

	xdims=(unsigned short *)rat_alloc(sizeof(unsigned short)*3);
	ydims=(unsigned short *)rat_alloc(sizeof(unsigned short)*3);

	xdims[0]=0; xdims[1]=WIDTH_MAX;
	ydims[0]=0; ydims[1]=HEIGHT_MAX;

	// add all dimensions on the loop
	for (;;)
	{
		SSegment *next_seg;

		xdims[num_dims-1]=this_node->x;
		ydims[num_dims-1]=this_node->y;

		if ((this_node==game->current_node)&&kickout) break;
		kickout=-1;

		next_seg=this_node->seg0==last_seg?this_node->seg1:this_node->seg0;
		this_node=next_seg->node0==this_node?next_seg->node1:next_seg->node0;
		last_seg=next_seg;

		numx=numy=++num_dims;
		xdims=(unsigned short *)rat_realloc((void *)xdims,sizeof(unsigned short)*num_dims);
		ydims=(unsigned short *)rat_realloc((void *)ydims,sizeof(unsigned short)*num_dims);
	}

	// sort the X/Y dimension lists
	if (num_dims<32)
	{
		rat_gnomesort((void *)xdims,num_dims,sizeof(unsigned short),compare_ushort);
		rat_gnomesort((void *)ydims,num_dims,sizeof(unsigned short),compare_ushort);
	}
	else
	{
		rat_qsort((void *)xdims,num_dims,sizeof(unsigned short),compare_ushort);
		rat_qsort((void *)ydims,num_dims,sizeof(unsigned short),compare_ushort);
	}

	// remove duplicate X's
	for (i=1; i<numx; i++)
	{
		if (xdims[i-1]==xdims[i])
		{
			for (j=i; j<numx; j++)
				xdims[j-1]=xdims[j];
			numx--;
			i--;
		}
	}

	// remove duplicate Y's
	for (i=1; i<numy; i++)
	{
		if (ydims[i-1]==ydims[i])
		{
			for (j=i; j<numy; j++)
				ydims[j-1]=ydims[j];
			numy--;
			i--;
		}
	}

	game->num_wall_quads=0;
	if (game->wall_quads)
	{
		for (i=0; i<game->num_wall_quads; i++)
		{
			rat_world_remove_and_destroy_body(game->phys_world,game->wall_quads[i]);
			rat_world_update(game->phys_world,0.0f,1);
		}
		rat_dealloc((void *)game->wall_quads);
	}
	game->wall_quads=NULL;

	contain_table=(char *)rat_alloc(sizeof(char)*numx*numy);

	for (i=0; i+1<numx; i++)
	{
		for (j=0; j+1<numy; j++)
		{
			if (abs(is_point_inside_loop(game,game->current_node,xdims[i+1]-1,ydims[j+1]-1))==4)
				contain_table[i+j*numx]=-1;
			else
				contain_table[i+j*numx]=0;
		}
	}

	for (i=0; i+1<numx; i++)
	{
		for (j=0; j+1<numy; j++)
		{
			if (contain_table[i+j*numx])
			{
				SQuad thisquad;

				thisquad.x0=xdims[i];
				thisquad.y0=ydims[j];

				for (j2=j; j2+2<numy; j2++)
				{
					if (!contain_table[i+(j2+1)*numx])
						break;
				}

				thisquad.x1=xdims[i+1];
				thisquad.y1=ydims[j2+1];

				j=j2;

				game->fill_quads=(SQuad *)rat_realloc((void *)game->fill_quads,sizeof(SQuad)*++quadctr);
				memcpy(game->fill_quads+quadctr-1,&thisquad,sizeof(SQuad));
			}
		}
	}
	game->num_fill_quads=quadctr;

	inflate_tesselate(game,numx,numy,xdims,ydims,contain_table);

	rat_dealloc((void *)contain_table);

	game->enclosure_area=0.0;
	for (i=0; i<game->num_fill_quads; i++)
	{
		game->enclosure_area+=
			((double)(game->fill_quads[i].x1-game->fill_quads[i].x0)/2.0)*
			((double)(game->fill_quads[i].y1-game->fill_quads[i].y0)/2.0);
	}
	capture_percent=(level_area-game->enclosure_area)/level_area*100.0;

	// swap the capture percent vars
	tempdbl=capture_percent;
	capture_percent=game->capture_percent;
	game->capture_percent=tempdbl;

	game->score+=(unsigned long)pow((game->capture_percent-capture_percent)*10.0,1.5);

	rat_dealloc((void *)xdims);
	rat_dealloc((void *)ydims);

	glDeleteLists(game->display_list,1);
	game->display_list=glGenLists(1);
	glNewList(game->display_list,GL_COMPILE);
		render_field(game);
	glEndList();
}

void ray_collision_occurs(rat_world *thisworld,rat_arbiter *arbiter,void *userdata)
{
	SGame *game=(SGame *)userdata;
	if (!(game->flags&GAME_FLAG_USE_GAMEPAD)) return;

	if ((arbiter->body_a==game->rays->bodies->body)||
		(arbiter->body_b==game->rays->bodies->body))
	{
		rat_set_rumble(0,USHRT_MAX/8,USHRT_MAX/4);
		is_colliding=1;
		rumble_start=clock();
	}
}

void ray_collision_persists(rat_world *thisworld,rat_arbiter *arbiter,void *userdata)
{
	SGame *game=(SGame *)userdata;
	if (!(game->flags&GAME_FLAG_USE_GAMEPAD)) return;

	if ((arbiter->body_a==game->rays->bodies->body)||
		(arbiter->body_b==game->rays->bodies->body))
	{
		is_colliding=1;
	}
}

void ray_collision_ending(rat_world *thisworld,rat_arbiter *arbiter,void *userdata)
{
	SGame *game=(SGame *)userdata;
	if (!(game->flags&GAME_FLAG_USE_GAMEPAD)) return;

	if ((arbiter->body_a==game->rays->bodies->body)||
		(arbiter->body_b==game->rays->bodies->body))
	{
		is_colliding=0;
	}
}
